﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using YK.Framework.Repository;
using System.Collections.Specialized;
using YK.Framework.Container;

namespace YK.Framework.Security
{
    /// <summary>
    ///  ASP.NET 应用程序的角色成员资格信息的存储进行管理。
    /// </summary>
    public class ClassicRoleProvider : System.Web.Security.RoleProvider
    {
        private string applicationName;
        private IRepository repository; 

       
        /// <summary>
        /// 获取或设置要存储和检索其角色信息的应用程序的名称。
        /// </summary>
        public override string ApplicationName
        {
            get
            {
                return applicationName;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("Provider application name not null.");
                }
                if (value.Length > 255)
                {
                    throw new System.Configuration.Provider.ProviderException("Provider application name too long.");
                }
                applicationName = value;

            }
        }


        /// <summary>
        /// 利用在 ASP.NET 应用程序的配置文件中指定的属性值初始化角色提供程序。
        /// 此方法不应从代码直接使用。
        /// </summary>
        /// <param name="name"></param>
        /// <param name="config"></param>
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (String.IsNullOrEmpty(name))
            {
                name = "HibernateRoleProvider";
            }

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "YK.Framework.Security Role Provider");
            }

            base.Initialize(name, config);


            this.applicationName = config["applicationName"];
            if (String.IsNullOrEmpty(this.applicationName))
            {
                this.applicationName = SecUtility.GetDefaultAppName();
            }

            if (this.applicationName.Length > 255)
            {
                throw new System.Configuration.Provider.ProviderException(
                    "Provider application name is too long, max length is 255.");
            }

            string strRepository = config["repository"];
            if (strRepository != null)
            {
                repository = ClassicContainer.GetObject(strRepository) as IRepository;
            }
            else
            {
                repository = ClassicContainer.GetObject<IRepository>() as IRepository;
            }

            if (!new ApplicationService(repository).ApplicationExists(this.applicationName))
            {
                new ApplicationService(repository).CreateApplication(this.applicationName);
            }

            config.Remove("repository");
            config.Remove("applicationName");           

            if (config.Count > 0)
            {
                string attribUnrecognized = config.GetKey(0);
                if (!String.IsNullOrEmpty(attribUnrecognized))
                {
                    throw new System.Configuration.Provider.ProviderException(
                        "Provider unrecognized attribute: " + attribUnrecognized);
                }
            }
        }

        /// <summary>
        /// 将指定用户名添加到每个指定的角色。
        /// </summary>
        /// <param name="usernames">一个字符串数组，其中包含要添加到指定角色的用户名。</param>
        /// <param name="roleNames">一个字符串数组，其中包含要将指定用户名添加到的角色名称。</param>
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            SecUtility.CheckArrayParameter(ref usernames, true, true, true, 255, "usernames");
            SecUtility.CheckArrayParameter(ref roleNames, true, true, true, 255, "roleNames");

            int status = new RoleService(repository).AddUsersToRoles(
                this.applicationName, 
                usernames, 
                roleNames);

            if (status != 0)
            {
                string errText = this.GetExceptionText(status);

                throw new System.Configuration.Provider.ProviderException(errText);
            }

        }

        /// <summary>
        /// 将新的角色添加到角色数据库。
        /// </summary>
        /// <param name="roleName">要创建的角色的名称。</param>
        public override void CreateRole(string roleName)
        {
            SecUtility.CheckParameter(ref roleName, true, true, true, 255, "roleName");

            int status = new RoleService(repository).CreateRole(
                this.applicationName, 
                roleName);

            if (status != 0)
            {
                string errText = this.GetExceptionText(status);

                throw new System.Configuration.Provider.ProviderException(errText);
            }
        }

        /// <summary>
        /// 从角色数据库移除一个角色。
        /// </summary>
        /// <param name="roleName">要删除的角色的名称。</param>
        /// <param name="throwOnPopulatedRole">
        /// 如果为 true，则当 roleName 包含一个或多个成员时将引发异常。 
        /// </param>
        /// <returns>如果成功删除角色，则为 true；否则为 false。</returns>
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            SecUtility.CheckParameter(ref roleName, true, true, true, 255, "roleName");

            int status = new RoleService(repository).DeleteRole(
                this.applicationName, 
                roleName, 
                throwOnPopulatedRole);

            if (status != 0)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 获取属于某个角色且与指定的用户名相匹配的用户名的数组。
        /// </summary>
        /// <param name="roleName">作为搜索范围的角色。</param>
        /// <param name="usernameToMatch">要搜索的用户名。</param>
        /// <returns>
        /// 一个字符串数组，包含用户名与 usernameToMatch 匹配且用户是指定角色的成员的所有用户的名称。 
        /// </returns>
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            SecUtility.CheckParameter(ref roleName, true, true, true, 255, "roleName");
            SecUtility.CheckParameter(ref usernameToMatch, true, true, false, 255, "usernameToMatch");

            return new RoleService(repository).FindUsersInRole(
                this.applicationName, 
                roleName, 
                usernameToMatch);
        }

        /// <summary>
        /// 获取应用程序的所有角色的列表。
        /// </summary>
        /// <returns>一个字符串数组，包含在特定应用程序的数据库中存储的所有角色的名称。</returns>
        public override string[] GetAllRoles()
        {
            return new RoleService(repository).GetAllRoles(
                this.applicationName);
        }

        /// <summary>
        /// 获取一个用户所属角色的列表。
        /// </summary>
        /// <param name="username">要为其返回角色列表的用户。</param>
        /// <returns>一个字符串数组，其中包含指定用户所属的所有角色的名称。</returns>
        public override string[] GetRolesForUser(string username)
        {
            SecUtility.CheckParameter(ref username, true, false, true, 255, "username");

            return new RoleService(repository).GetRolesForUser(
                this.applicationName, 
                username);
        }

        /// <summary>
        /// 获取属于指定角色的用户的列表。
        /// </summary>
        /// <param name="roleName">一个角色名称，将获取该角色的用户列表。</param>
        /// <returns>一个字符串数组，其中包含指定角色拥有的所有成员的用户名。</returns>
        public override string[] GetUsersInRole(string roleName)
        {
            SecUtility.CheckParameter(ref roleName, true, true, true, 255, "roleName");

            return new RoleService(repository).GetUsersInRole(
                this.applicationName, 
                roleName);
        }

        /// <summary>
        /// 获取一个指示指定用户是否属于指定角色的值。
        /// </summary>
        /// <param name="username">要搜索的用户名。</param>
        /// <param name="roleName">作为搜索范围的角色。</param>
        /// <returns>如果指定的用户名属于指定角色，则为 true；否则为 false。 </returns>
        public override bool IsUserInRole(string username, string roleName)
        {
            SecUtility.CheckParameter(ref roleName, true, true, true, 255, "roleName");
            SecUtility.CheckParameter(ref username, true, false, true, 255, "username");

            return new RoleService(repository).IsUserInRole(this.applicationName, username, roleName);
        }

        /// <summary>
        /// 移除指定角色中的指定用户名。
        /// </summary>
        /// <param name="usernames">一个字符串数组，其中包含要从指定角色中移除的用户名。</param>
        /// <param name="roleNames">一个字符串数组，其中包含要将指定用户名从中移除的角色的名称。</param>
        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            SecUtility.CheckArrayParameter(ref usernames, true, true, true, 255, "usernames");
            SecUtility.CheckArrayParameter(ref roleNames, true, true, true, 255, "roleNames");

            int status = new RoleService(repository).RemoveUsersFromRoles(this.applicationName, usernames, roleNames);

            if (status != 0)
            {
                string errText = this.GetExceptionText(status);

                throw new System.Configuration.Provider.ProviderException(errText);
            }

        }

        /// <summary>
        /// 获取一个值，该值指示指定的角色名称是否已存在于角色数据库中。
        /// </summary>
        /// <param name="roleName">要在数据库中搜索的角色的名称。</param>
        /// <returns>如果角色名称已存在于数据库中，则为 true；否则为 false。 </returns>
        public override bool RoleExists(string roleName)
        {
            SecUtility.CheckParameter(ref roleName, true, true, true, 255, "roleName");

            return new RoleService(repository).RoleExists(this.applicationName, roleName);
        }

        private string GetExceptionText(int status)
        {
            string errText;
            switch (status)
            {
                case 0:
                    return String.Empty;

                case 1:
                    errText = "User not found.";
                    break;

                case 2:
                    errText = "Role not found.";
                    break;

                case 3:
                    errText = "This user already in role.";
                    break;

                case 4:
                    errText = "Role is not empty.";
                    break;

                case 5:
                    errText = "Role already exists.";
                    break;

                default:
                    errText = "Provider error.";
                    break;
            }
            return errText;
        }
    }
}